package cn.apthink.utility;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Locale;

/**
 * 字节处理工具类
 * @author david
 *
 */
public class ByteUtil {

	/**
	 * Short转Byte[]
	 *
	 * @param number
	 * @return
	 */
	public static byte[] short2Byte(short number) {
		byte[] bytes = new byte[2];
		// 通过移位运算，截取低8位的方式，将short保存到byte数组
		bytes[0] = (byte) (number);
		bytes[1] = (byte) (number >>> 8);
		return bytes;
	}

	/**
	 * Byte[]转Short
	 *
	 * @param intByte
	 * @return
	 */
	public static short byte2Short(byte[] bytes) {
		int result = 0;
		// 将每个byte依次搬运到short相应的位置
		result = bytes[0] & 0xff;
		if (bytes.length > 1)
			result = result | (bytes[1] << 8);
		return (short) result;
	}

	/**
	 * Integer转Byte[]
	 *
	 * @param intByte
	 * @return
	 */
	public static byte[] int2Byte(int number) {
		byte[] bytes = new byte[4];
		// 通过移位运算，截取低8位的方式，将int保存到byte数组
		for (int i = 0; i < 4; i++) {
			bytes[i] = (byte) (number >>> i * 8);
		}
		return bytes;
	}

	/**
	 * Byte[]转Integer
	 *
	 * @param intByte
	 * @return
	 */
	public static int byte2Int(byte[] bytes) {
		int result = 0;
		// 将每个byte依次搬运到int相应的位置
		int len = 0;
		int byteLen = bytes.length;
		while (byteLen > len && len < 4) {
			result = result | ((bytes[len] & 0xff) << 8 * len);
			len++;
		}
		return result;
	}

	/**
	 * Long转Byte[]
	 *
	 * @param num
	 * @return
	 */
	public static byte[] long2Byte(long number) {
		byte[] bytes = new byte[8];
		// 通过移位运算，截取低8位的方式，将int保存到byte数组
		for (int i = 0; i < 8; i++) {
			bytes[i] = (byte) (number >>> i * 8);
		}
		return bytes;
	}

	/**
	 * Byte[]转Long
	 *
	 * @param b
	 * @return
	 */
	public static long byte2Long(byte[] bytes) {
		long result = 0;
		// 将每个byte依次搬运到int相应的位置
		int len = 0;
		int byteLen = bytes.length;
		while (byteLen > len && len < 8) {
			result = result | ((long) (bytes[len] & 0xff) << 8 * len);
			len++;
		}
		return result;
	}

	/**
	 * Double转Byte[]
	 *
	 * @param d
	 * @return
	 */
//	public static byte[] double2Byte(double d) {
//		byte[] b = new byte[8];
//		long l = Double.doubleToLongBits(d);
//		for (int i = 0; i < b.length; i++) {
//			b[i] = new Long(l).byteValue();
//			l = l >>> 8;
//		}
//		return b;
//	}

	/**
	 * Byte[]转Double
	 *
	 * @param b
	 * @return
	 */
	public static double byte2Double(byte[] b) {
		long l;
		l = b[0];
		l &= 0xff;
		l |= ((long) b[1] << 8);
		l &= 0xffff;
		l |= ((long) b[2] << 16);
		l &= 0xffffff;
		l |= ((long) b[3] << 24);
		l &= 0xffffffffl;
		l |= ((long) b[4] << 32);
		l &= 0xffffffffffl;
		l |= ((long) b[5] << 40);
		l &= 0xffffffffffffl;
		l |= ((long) b[6] << 48);
		l &= 0xffffffffffffffl;
		l |= ((long) b[7] << 56);
		return Double.longBitsToDouble(l);
	}

	/**
	 * Byte[]转String
	 *
	 * @param b
	 * @param charsetName "GBK"
	 * @return
	 */
	public static String byte2String(byte[] b, String charsetName) {
		String str = null;
		if (b != null && b.length>0) {
			try {
				if (Util.isEmpty(charsetName)) {
					charsetName = "GBK";
				}
				str = new String(b, charsetName);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return str;
	}

	/**
	 * Byte[]转String
	 *
	 * @param b
	 * @param charsetName "GBK"
	 * @return
	 */
	public static String byte2String(byte[] b) {
		return byte2String(b, "UTF-8");
	}

	/**
	 * String转Byte[]
	 *
	 * @param b
	 * @return
	 */
	public static byte[] string2Byte(String s) {
		byte[] bytes = string2Byte(s, "UTF-8"); // 数据代理使用GBK编码方式获取值
		return bytes;
	}

	/**
	 * String转Byte[]
	 *
	 * @param b
	 * @return
	 */
	public static byte[] string2Byte(String s, String charsetName) {
		byte[] bytes = null;
		try {
			if (Util.isEmpty(charsetName)) {
				charsetName = "UTF-8";// 数据代理使用GBK编码方式获取值
			}
			bytes = s.getBytes(charsetName);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bytes;
	}

	/**
	 * Blob转成byte数组
	 *
	 * @param blob
	 * @return
	 */
	public static byte[] blob2Bytes(Blob blob) {
		byte[] b = null;
		try {
			b = blob.getBytes(1L, (int) blob.length());
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return b;
	}

	/**
	 * Clob转成byte数组
	 *
	 * @param clob
	 * @param charsetName
	 * @return
	 */
	public static byte[] clob2Bytes(Clob clob, String charsetName) {
		byte[] b = null;
		try {
			if (clob == null)
				return null;
			String retVal = (clob != null ? clob.getSubString(1, (int) clob.length()) : null);
			if (Util.isEmpty(retVal)) {
				return null;
			}
			b = retVal.getBytes(Util.isEmpty(charsetName) ? "ISO-8859-1" : charsetName);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return b;
	}

	/**
	 * Clob转成byte数组
	 *
	 * @param clob
	 * @return
	 */
	public static byte[] clob2Bytes(Clob clob) {
		return clob2Bytes(clob, "ISO-8859-1");
	}

	/**
	 * 合并字节数组
	 * 
	 * @param firstArray  第一个数组
	 * @param secondArray 第二个数组
	 * @return byte[]
	 */
	public static byte[] concat(byte[] firstArray, byte[] secondArray) {
		if (firstArray == null || secondArray == null) {
			return null;
		}
		byte[] bytes = new byte[firstArray.length + secondArray.length];
		System.arraycopy(firstArray, 0, bytes, 0, firstArray.length);
		System.arraycopy(secondArray, 0, bytes, firstArray.length, secondArray.length);
		return bytes;
	}

	public static byte[] localDateTime2Byte(LocalDateTime ldt) {
		byte[] bytes = new byte[8];
		if (ldt != null) {
			long l = ldt.toInstant(ZoneOffset.UTC).toEpochMilli();
			bytes = long2Byte(l);
		}
		return bytes;
	}

	public static LocalDateTime byte2LocalDateTime(byte[] data) {
		long l = byte2Long(data);
		return LocalDateTime.ofInstant(Instant.ofEpochMilli(l), ZoneOffset.UTC);
	}

	/**
	 * 从输入流中读取到字节数组中
	 * 
	 * @param in     输入流
	 * @param length 读取长度
	 * @return
	 * @throws IOException
	 */
	public static byte[] readBytes(InputStream in, long length) throws IOException {
		ByteArrayOutputStream bo = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int read = 0;
		while (read < length) {
			int cur = in.read(buffer, 0, (int) Math.min(1024, length - read));
			if (cur < 0) {
				break;
			}
			read += cur;
			bo.write(buffer, 0, cur);
		}
		return bo.toByteArray();
	}

	/**
	 * 十六进制字符串转byte[]
	 * 
	 * @param hex 十六进制字符串
	 * @return byte[]
	 */
	public static byte[] hexStr2Bytes(String hex) {
		if (hex == null) {
			return new byte[] {};
		}

		// 奇数位补0
		if (hex.length() % 2 != 0) {
			hex = "0" + hex;
		}

		int length = hex.length();
		ByteBuffer buffer = ByteBuffer.allocate(length / 2);
		for (int i = 0; i < length; i++) {
			String hexStr = hex.charAt(i) + "";
			i++;
			hexStr += hex.charAt(i);
			byte b = (byte) Util.Str2Int(hexStr, 16);
			buffer.put(b);
		}
		return buffer.array();
	}

	/**
	 * byte[]转十六进制字符串
	 * 
	 * @param array byte[]
	 * @return 十六进制字符串
	 */
	public static String byte2HexStr(byte[] array) {
		if (array == null) {
			return "";
		}
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < array.length; i++) {
			buffer.append(" " + byte2Hex(array[i]));
		}
		return buffer.length() > 0 ? buffer.substring(1) : "";
	}

	/**
	 * byte转十六进制字符
	 * 
	 * @param b byte
	 * @return 十六进制字符
	 */
	public static String byte2Hex(byte b) {
		String hex = Integer.toHexString(b & 0xFF);
		if (hex.length() == 1) {
			hex = '0' + hex;
		}
		return hex.toUpperCase(Locale.getDefault());
	}

	/**
	 * 返回校验码字符串
	 * 
	 * @param bytes
	 * @return
	 */
	public static String getCRC(byte[] bytes) {
		int CRC = 0x0000ffff;
		int POLYNOMIAL = 0x0000a001;

		int i, j;
		for (i = 0; i < bytes.length; i++) {
			CRC ^= ((int) bytes[i] & 0x000000ff);
			for (j = 0; j < 8; j++) {
				if ((CRC & 0x00000001) != 0) {
					CRC >>= 1;
					CRC ^= POLYNOMIAL;
				} else {
					CRC >>= 1;
				}
			}
		}
		CRC = ((CRC & 0xFF00) >> 8) | ((CRC & 0x00FF) << 8);
		return Integer.toHexString(CRC);
	}

	/**
	 * 返回校验码字节数组
	 * 
	 * @param data
	 * @return
	 */
	public static byte[] getCRCByte(byte[] data) {
		byte[] crc16_h = { (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
				(byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
				(byte) 0x40 };

		byte[] crc16_l = { (byte) 0x00, (byte) 0xC0, (byte) 0xC1, (byte) 0x01, (byte) 0xC3, (byte) 0x03, (byte) 0x02,
				(byte) 0xC2, (byte) 0xC6, (byte) 0x06, (byte) 0x07, (byte) 0xC7, (byte) 0x05, (byte) 0xC5, (byte) 0xC4,
				(byte) 0x04, (byte) 0xCC, (byte) 0x0C, (byte) 0x0D, (byte) 0xCD, (byte) 0x0F, (byte) 0xCF, (byte) 0xCE,
				(byte) 0x0E, (byte) 0x0A, (byte) 0xCA, (byte) 0xCB, (byte) 0x0B, (byte) 0xC9, (byte) 0x09, (byte) 0x08,
				(byte) 0xC8, (byte) 0xD8, (byte) 0x18, (byte) 0x19, (byte) 0xD9, (byte) 0x1B, (byte) 0xDB, (byte) 0xDA,
				(byte) 0x1A, (byte) 0x1E, (byte) 0xDE, (byte) 0xDF, (byte) 0x1F, (byte) 0xDD, (byte) 0x1D, (byte) 0x1C,
				(byte) 0xDC, (byte) 0x14, (byte) 0xD4, (byte) 0xD5, (byte) 0x15, (byte) 0xD7, (byte) 0x17, (byte) 0x16,
				(byte) 0xD6, (byte) 0xD2, (byte) 0x12, (byte) 0x13, (byte) 0xD3, (byte) 0x11, (byte) 0xD1, (byte) 0xD0,
				(byte) 0x10, (byte) 0xF0, (byte) 0x30, (byte) 0x31, (byte) 0xF1, (byte) 0x33, (byte) 0xF3, (byte) 0xF2,
				(byte) 0x32, (byte) 0x36, (byte) 0xF6, (byte) 0xF7, (byte) 0x37, (byte) 0xF5, (byte) 0x35, (byte) 0x34,
				(byte) 0xF4, (byte) 0x3C, (byte) 0xFC, (byte) 0xFD, (byte) 0x3D, (byte) 0xFF, (byte) 0x3F, (byte) 0x3E,
				(byte) 0xFE, (byte) 0xFA, (byte) 0x3A, (byte) 0x3B, (byte) 0xFB, (byte) 0x39, (byte) 0xF9, (byte) 0xF8,
				(byte) 0x38, (byte) 0x28, (byte) 0xE8, (byte) 0xE9, (byte) 0x29, (byte) 0xEB, (byte) 0x2B, (byte) 0x2A,
				(byte) 0xEA, (byte) 0xEE, (byte) 0x2E, (byte) 0x2F, (byte) 0xEF, (byte) 0x2D, (byte) 0xED, (byte) 0xEC,
				(byte) 0x2C, (byte) 0xE4, (byte) 0x24, (byte) 0x25, (byte) 0xE5, (byte) 0x27, (byte) 0xE7, (byte) 0xE6,
				(byte) 0x26, (byte) 0x22, (byte) 0xE2, (byte) 0xE3, (byte) 0x23, (byte) 0xE1, (byte) 0x21, (byte) 0x20,
				(byte) 0xE0, (byte) 0xA0, (byte) 0x60, (byte) 0x61, (byte) 0xA1, (byte) 0x63, (byte) 0xA3, (byte) 0xA2,
				(byte) 0x62, (byte) 0x66, (byte) 0xA6, (byte) 0xA7, (byte) 0x67, (byte) 0xA5, (byte) 0x65, (byte) 0x64,
				(byte) 0xA4, (byte) 0x6C, (byte) 0xAC, (byte) 0xAD, (byte) 0x6D, (byte) 0xAF, (byte) 0x6F, (byte) 0x6E,
				(byte) 0xAE, (byte) 0xAA, (byte) 0x6A, (byte) 0x6B, (byte) 0xAB, (byte) 0x69, (byte) 0xA9, (byte) 0xA8,
				(byte) 0x68, (byte) 0x78, (byte) 0xB8, (byte) 0xB9, (byte) 0x79, (byte) 0xBB, (byte) 0x7B, (byte) 0x7A,
				(byte) 0xBA, (byte) 0xBE, (byte) 0x7E, (byte) 0x7F, (byte) 0xBF, (byte) 0x7D, (byte) 0xBD, (byte) 0xBC,
				(byte) 0x7C, (byte) 0xB4, (byte) 0x74, (byte) 0x75, (byte) 0xB5, (byte) 0x77, (byte) 0xB7, (byte) 0xB6,
				(byte) 0x76, (byte) 0x72, (byte) 0xB2, (byte) 0xB3, (byte) 0x73, (byte) 0xB1, (byte) 0x71, (byte) 0x70,
				(byte) 0xB0, (byte) 0x50, (byte) 0x90, (byte) 0x91, (byte) 0x51, (byte) 0x93, (byte) 0x53, (byte) 0x52,
				(byte) 0x92, (byte) 0x96, (byte) 0x56, (byte) 0x57, (byte) 0x97, (byte) 0x55, (byte) 0x95, (byte) 0x94,
				(byte) 0x54, (byte) 0x9C, (byte) 0x5C, (byte) 0x5D, (byte) 0x9D, (byte) 0x5F, (byte) 0x9F, (byte) 0x9E,
				(byte) 0x5E, (byte) 0x5A, (byte) 0x9A, (byte) 0x9B, (byte) 0x5B, (byte) 0x99, (byte) 0x59, (byte) 0x58,
				(byte) 0x98, (byte) 0x88, (byte) 0x48, (byte) 0x49, (byte) 0x89, (byte) 0x4B, (byte) 0x8B, (byte) 0x8A,
				(byte) 0x4A, (byte) 0x4E, (byte) 0x8E, (byte) 0x8F, (byte) 0x4F, (byte) 0x8D, (byte) 0x4D, (byte) 0x4C,
				(byte) 0x8C, (byte) 0x44, (byte) 0x84, (byte) 0x85, (byte) 0x45, (byte) 0x87, (byte) 0x47, (byte) 0x46,
				(byte) 0x86, (byte) 0x82, (byte) 0x42, (byte) 0x43, (byte) 0x83, (byte) 0x41, (byte) 0x81, (byte) 0x80,
				(byte) 0x40 };

		int crc = 0x0000ffff;
		int ucCRCHi = 0x00ff;
		int ucCRCLo = 0x00ff;
		int iIndex;
		for (int i = 0; i < data.length; ++i) {
			iIndex = (ucCRCLo ^ data[i]) & 0x00ff;
			ucCRCLo = ucCRCHi ^ crc16_h[iIndex];
			ucCRCHi = crc16_l[iIndex];
		}

		crc = ((ucCRCHi & 0x00ff) << 8) | (ucCRCLo & 0x00ff) & 0xffff;
		// 高低位互换，输出符合相关工具对Modbus CRC16的运算
//		crc = ((crc & 0xFF00) >> 8) | ((crc & 0x00FF) << 8);
//		return String.format("%04X", crc);
		return short2Byte((short) crc);
	}

	public static void main(String args[]) {
		String s = "15 00 BF 05 01 00 15 01 0C 0B 30 35 51 04 00 00 00 31 32 33 34 35 20";
		byte[] buf = hexStr2Bytes(s.replace(" ", ""));
		System.out.println(byte2HexStr(buf));
		System.out.println(byte2HexStr(getCRCByte(buf)));
		long sh = Long.MIN_VALUE;
		byte[] bsh = long2Byte(sh);
		long nsh = byte2Long(bsh);
		System.out.print(sh);
		System.out.print(byte2HexStr(bsh));
		System.out.println(nsh);

		LocalDateTime d = LocalDateTime.now();
		byte[] bd = localDateTime2Byte(d);
		LocalDateTime nd = byte2LocalDateTime(bd);
		System.out.print(d);
		System.out.print(byte2HexStr(bd));
		System.out.println(nd);

	}
}
